package com.yfp.operation.service.impl;

import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfp.common.core.constant.CacheConstants;
import com.yfp.common.core.domain.R;
import com.yfp.common.core.utils.bean.BeanUtils;
import com.yfp.common.security.utils.SecurityUtils;
import com.yfp.operation.domain.ClientCoupon;
import com.yfp.operation.domain.MarCoupon;
import com.yfp.operation.domain.MarCouponDirectional;
import com.yfp.operation.domain.dto.GrantOrienteeringDTO;
import com.yfp.operation.domain.dto.MarCouponDirectionalDTO;
import com.yfp.operation.domain.vo.ClientCouponNumVO;
import com.yfp.operation.domain.vo.MarCouponDirectionalVO;
import com.yfp.operation.domain.vo.TemplateVerifyVO;
import com.yfp.operation.mapper.*;
import com.yfp.operation.mapper.*;
import com.yfp.operation.service.MarCouponDirectionalService;
import com.yfp.operation.service.WxInfoService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.RedissonMultiLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author admin
 * @description 针对表【mar_coupon_directional(优惠券定向发放)】的数据库操作Service实现
 * @createDate 2024-09-13 10:55:06
 */
@Service
@Slf4j
public class MarCouponDirectionalServiceImpl extends ServiceImpl<MarCouponDirectionalMapper, MarCouponDirectional> implements MarCouponDirectionalService {

    @Resource
    private MarCouponDirectionalMapper marCouponDirectionalMapper;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private ClientMapper clientMapper;
    @Resource
    private ClientCouponMapper clientCouponMapper;
    @Resource
    private MarCouponMapper marCouponMapper;
    @Resource
    private OrderComboMapper orderComboMapper;
    @Resource
    private WxInfoService wxInfoService;

    /**
     * 添加定向发放优惠券列表
     *
     * @param mobileList
     */
    @Override
    @Transactional
    public void directionalAdd(List<String> mobileList, Long couponId) {

        // 插入定向发放列表锁
        List<String> lockKeys = mobileList.stream().map(item -> CacheConstants.MAR_COUPON_DIRECTIONAL_LOCK_KEY + item).collect(Collectors.toList());
        List<RLock> rLocks = lockKeys.stream().map(lockKey -> {
            RLock lock = redissonClient.getLock(lockKey);
            return lock;
        }).collect(Collectors.toList());
        RedissonMultiLock redissonMultiLock = new RedissonMultiLock(rLocks.toArray(new RLock[rLocks.size()]));
        Assert.isTrue(redissonMultiLock.tryLock(), "稍后重试");

        try {
            Long userId = SecurityUtils.getLoginUser().getSysUser().getUserId();
//            Long userId = 58L;

            // 根据手机号获取用户信息
            List<MarCouponDirectional> directionalList = clientMapper.getByMobiles(mobileList);
            Map<String, MarCouponDirectional> directionalMap = directionalList.stream().collect(Collectors.toMap(MarCouponDirectional::getMobile, Function.identity()));

            List<MarCouponDirectional> directionalArrayList = new ArrayList<>();
            // 遍历号码
            for (String mobile : mobileList) {
                log.info("添加手机号:{}", mobile);
                // 如果map包含该号码，则补充数据
                if (directionalMap.containsKey(mobile)) {
                    MarCouponDirectional directional = directionalMap.get(mobile);
                    directional.setStatus(1);
                    directional.setCreateUserId(userId);
                    directional.setUpdateUserId(userId);
                    directional.setMarCouponId(couponId);
                    directionalArrayList.add(directional);
                } else {
                    // 不包含则创建对象添加进集合
                    MarCouponDirectional directional = new MarCouponDirectional();
                    directional.setStatus(1);
                    directional.setCreateUserId(userId);
                    directional.setUpdateUserId(userId);
                    directional.setMarCouponId(couponId);
                    directional.setMobile(mobile);
                    directionalArrayList.add(directional);
                }
            }
            log.info("插入数据:{]", directionalList);
            // 批量新增
            marCouponDirectionalMapper.adds(directionalArrayList);
        } finally {
            // 释放资源
            redissonMultiLock.unlock();
        }
    }


    /**
     * 定向发放列表
     *
     * @param marCouponDirectionalDTO
     * @return
     */
    @Override
    public List<MarCouponDirectionalVO> directionalList(MarCouponDirectionalDTO marCouponDirectionalDTO) {
        List<MarCouponDirectionalVO> list = marCouponDirectionalMapper.directionalList(marCouponDirectionalDTO);
        return list;
    }

    /**
     * 获取该优惠券定向发放的手机号
     *
     * @return
     */
    @Override
    public List<String> getMobiles(Long couponId) {
        List<String> mobiles = marCouponDirectionalMapper.getMobileAll(couponId);
        return mobiles;
    }

    /**
     * 定向批量发放优惠券
     *
     * @param couponId
     * @return
     */
    @Override
    @Transactional
    public R<Integer> grantOrienteering(Long couponId) {
        // 优惠券剩余数量锁
        RLock lock = redissonClient.getLock(CacheConstants.MAR_COUPON_SURPLUSCOUNT_LOCK_KEY + couponId);
        Assert.isTrue(lock.tryLock(), "稍后重试");
        try {

            // 优惠券信息
            MarCoupon marCoupon = marCouponMapper.selectById(couponId);
            long time = new Date().getTime();
            // 校验是否失效、是否过期
            if (marCoupon.getStatus().equals(2) || (null == marCoupon.getValidDays() && marCoupon.getValidEndTime().getTime() < time)) {
                return R.ok(null, "发放失败，优惠券失效或过期");
            }

            MarCouponDirectionalDTO marCouponDirectionalDTO = new MarCouponDirectionalDTO();
            marCouponDirectionalDTO.setCouponId(couponId);
            marCouponDirectionalDTO.setStatus(1);
            // 定向发放列表
            List<MarCouponDirectionalVO> directionalList = marCouponDirectionalMapper.directionalList(marCouponDirectionalDTO);
            if (directionalList.isEmpty()) {
                return R.ok(0);
            }
            // 发放数量大于优惠券数量
            if (directionalList.size() > marCoupon.getSurplusCount()) {
                return R.ok(null, "发放失败，优惠券数量不足");
            }
            // 超出单个用户领取数
            // 正常发放list
            List<MarCouponDirectionalVO> directionalVOS = new ArrayList<>();
            // 超出领取 list
            List<MarCouponDirectionalVO> directionalVOS2 = new ArrayList<>();

            Map<String, List<MarCouponDirectionalVO>> clientMap = directionalList.stream()
                    .collect(Collectors.groupingBy(MarCouponDirectionalVO::getMobile));
            // 统计每个用户领取的数量
            List<ClientCouponNumVO> couponNumVOS = clientCouponMapper.getCouponNum(directionalList, couponId);
            Map<String, ClientCouponNumVO> couponNumVOMap = couponNumVOS.stream()
                    .collect(Collectors.toMap(ClientCouponNumVO::getClientId, Function.identity()));
            for (String key : clientMap.keySet()) {
                List<MarCouponDirectionalVO> marCouponDirectionalVOS = clientMap.get(key);
                for (int i = 0; i < marCouponDirectionalVOS.size(); i++) {
                    int couponNum = i;
                    if (null != marCouponDirectionalVOS.get(0) && marCouponDirectionalVOS.get(0).getIsClient() == 1) {
                        Integer couponNum1 =
                                null == couponNumVOMap.get(marCouponDirectionalVOS.get(0).getClientId()) ||
                                        null == couponNumVOMap.get(marCouponDirectionalVOS.get(0).getClientId()).getCouponNum()
                                        ? 0 : couponNumVOMap.get(marCouponDirectionalVOS.get(0).getClientId()).getCouponNum();
                        couponNum = couponNum + couponNum1;
                    }
                    if (couponNum >= marCoupon.getReceiveCount()) {
                        directionalVOS2.add(marCouponDirectionalVOS.get(i));
                    } else {
                        directionalVOS.add(marCouponDirectionalVOS.get(i));
                    }
                }
            }

            ClientCoupon clientCoupon = new ClientCoupon();
            // 领取后指定天数有效时间处理
            if (null != marCoupon.getValidDays()) {
                // 当前日期
                LocalDate currentDate = LocalDate.now();
                // 要添加的天数
                int daysToAdd = marCoupon.getValidDays();
                // 当前日期加上指定天数
                LocalDate newDate = currentDate.plusDays(daysToAdd);
                // 获取结果日期的起始时间（00:00:00）
                LocalDateTime startOfDay = newDate.atStartOfDay();
                LocalDateTime localDateTime = startOfDay.minusSeconds(1);
                // 转换为Instant
                Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
                // 转换为Timestamp
                Timestamp timestamp = Timestamp.from(instant);
                marCoupon.setValidStartTime(new Timestamp(System.currentTimeMillis()));
                marCoupon.setValidEndTime(timestamp);
            }


            BeanUtils.copyBeanProp(clientCoupon, marCoupon);
            clientCoupon.setStatus(marCoupon.getStatus());
            clientCoupon.setCouponId(couponId.longValue());
            clientCoupon.setPostponeDay(0);


            GrantOrienteeringDTO failDTO = new GrantOrienteeringDTO();

            // 获取不是小程序用户的手机号
            List<MarCouponDirectionalVO> notClientList = directionalVOS.stream().filter(item -> item.getIsClient().equals(0)).toList();

            List<Long> notClientDirectionalIds = notClientList.stream().map(s -> s.getId()).toList();
            if (!CollectionUtils.isEmpty(notClientDirectionalIds)) {
                failDTO.setIds(notClientDirectionalIds);
                failDTO.setStatus(2);
                failDTO.setSendTime(null);
                marCouponDirectionalMapper.updateStatus(failDTO);
            }
            // 获取是小程序用户
            List<MarCouponDirectionalVO> yesClients = directionalVOS.stream().filter(item -> item.getIsClient().equals(1)).toList();
            // 获取老用户id
            List<String> userIds = orderComboMapper.agedClientId();

            List<String> userIdList = null;
            List<Long> ids = null;
            List<MarCouponDirectionalVO> couponDirectionalVOS = null;
            if (marCoupon.getClientStatus() == 2) {
                // 筛选新用户
                couponDirectionalVOS = yesClients.stream().filter(item -> !userIds.contains(item.getClientId())).toList();
//                userIdList = couponDirectionalVOS.stream().map(s->s.getClientId()).toList();
                ids = couponDirectionalVOS.stream().map(s -> s.getId()).toList();

                // 老用户
                List<MarCouponDirectionalVO> orgs = yesClients.stream().filter(item -> userIds.contains(item.getClientId())).toList();
                directionalVOS2.addAll(orgs);
            } else {
                couponDirectionalVOS = yesClients;
                ids = yesClients.stream().map(s -> s.getId()).toList();
            }

            // 失败列表
            List<Long> notFail = directionalVOS2.stream().map(s -> s.getId()).toList();
            List<String> notifyCouponArriveReminderList = new ArrayList<>();

            if (!couponDirectionalVOS.isEmpty()) {
                // 发放验证， 成功发放，失败添加到失败列表notFail
                // 判断数量、用户领取次数
                for (MarCouponDirectionalVO couponDirectionalVO : couponDirectionalVOS) {
                    // 获取优惠券剩余数量
                    MarCoupon mc = marCouponMapper.selectById(couponId);
                    if (mc.getSurplusCount() > 0) {
                        // 获取用户领取该券的数量
                        Integer acquireNum = clientCouponMapper.acquireNum(couponDirectionalVO.getClientId(), mc.getId());
                        if (mc.getReceiveCount() <= acquireNum) {
                            notFail.add(couponDirectionalVO.getId());
                            ids.remove(couponDirectionalVO.getId());
                            continue;
                        }
                        // 发券
                        clientCouponMapper.add(couponDirectionalVO.getClientId(), clientCoupon);
                        // 定向给用户发送优惠券到账提醒
                        notifyCouponArriveReminderList.add(couponDirectionalVO.getClientId());
                    } else {
                        notFail.add(couponDirectionalVO.getId());
                    }
                }
            }

            failDTO.setStatus(3);
            failDTO.setSendTime(new Date());
            failDTO.setIds(ids);
            if (!CollectionUtils.isEmpty(failDTO.getIds())) {
                marCouponDirectionalMapper.updateStatus(failDTO);
                Integer surplusCount = marCoupon.getSurplusCount();
                LambdaUpdateWrapper<MarCoupon> updateWrapper = new LambdaUpdateWrapper<MarCoupon>()
                        .set(MarCoupon::getSurplusCount, surplusCount - ids.size())
                        .eq(MarCoupon::getId, marCoupon.getId());
                marCouponMapper.update(null, updateWrapper);
            }


            // 失败
            if (!CollectionUtils.isEmpty(notFail)) {
                failDTO.setIds(notFail);
                failDTO.setStatus(4);
                failDTO.setSendTime(null);
                marCouponDirectionalMapper.updateStatus(failDTO);
            }

            // 调用微信接口，发送优惠券到账提醒
            wxInfoService.notifyCouponArriveReminder(notifyCouponArriveReminderList, clientCoupon);
            return R.ok(yesClients.size());
        } finally {
            lock.unlock();
        }
    }


    // 中国大陆手机号码正则表达式
    private static final String CHINA_MOBILE_NUMBER_REGEX = "^1[3-9]\\d{9}$";

    public static boolean isValidChinaMobileNumber(String number) {
        if (number == null || number.isEmpty()) {
            return false;
        }
        return number.matches(CHINA_MOBILE_NUMBER_REGEX);
    }

    /**
     * 校验模板
     *
     * @param list
     * @param couponId
     * @return
     */
    @Override
    public R<TemplateVerifyVO> templateVerify(List<Object> list, Long couponId) {
        log.info("开始校验：{}", list.size());
        TemplateVerifyVO verifyVO = new TemplateVerifyVO();
        try {
            verifyVO.setVerifyNum(list.size());
            verifyVO.setClientNum(0);
            List<String> strings = new ArrayList<>();
            for (Object t : list) {
                if (!isValidChinaMobileNumber(t.toString())) {
                    return R.ok(null, "内容格式错误！");
                }
                if (!strings.contains(t.toString())) {
                    strings.add(t.toString());
                }
            }
            verifyVO.setClientNum(strings.size());
            return R.ok(verifyVO);
        } catch (Exception e) {
            e.printStackTrace();
            return R.ok(null, "内容格式错误！");
        }
    }

    /**
     * 删除定向发放列表
     *
     * @param id
     */
    @Override
    @Transactional
    public void deleteById(Long id) {
        marCouponDirectionalMapper.deleteByIdMarCouponDirectional(id);
    }

    @Override
    public void bulkDelByMarCouponId(Long couponId) {
        marCouponDirectionalMapper.bulkDelByMarCouponId(couponId);
    }
}




